define([], function () {
    function newEvent(obj) {
        var event = {
            invalidate: function () {
                this._invalid_ = true;
                if (this.originalEvent) {
                    this.originalEvent._invalid_ = true;
                }
            }
        };
        var keys = Object.keys(obj || {});
        for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
            event[key] = obj[key];
        }
        return event;
    }

    function ArrayAlloc(identifier) {
        var recycleIds = [];
        var nextId = 0;
        this.items = [];
        var self = this;
        this.dequeueId = function () {
            if (recycleIds.length) {
                return recycleIds.pop();
            }
            return nextId++;
        };
        this.recycleId = function (id) {
            if (self.items[id] === undefined) {
                if (id) {
                    recycleIds.push(id);
                }
            }
        };
        this.addItem = function (item) {
            var id = self.dequeueId();
            self.items[id] = item;
            item[identifier] = id;
        };
        this.removeItem = function (item) {
            var id = item[identifier];
            item[identifier] = undefined;
            self.items[id] = undefined;
            this.recycleId(id);
        };
        this.isEmpty = function () {
            if (!self.items.length) return true;
            var items = self.items || [];
            for (var i = 0; i < items.length; i++) {
                if (items[i]) return false;
            }
            return true;
        };
    }

    function EventDispatcher(id, self) {
        var dispatcher = {};

        self.dispatcher = dispatcher;

        self = self || this;
        self.on = function (event, handler) {
            if (!dispatcher[event]) {
                var key = id + "_" + event;
                dispatcher[event] = new ArrayAlloc(key);
            }
            dispatcher[event].addItem(handler);
            return self;
        };
        self.off = function (event, handler) {
            if (dispatcher[event]) {
                dispatcher[event].removeItem(handler);
            }
            return self;
        };
        self.emit = function (event) {
            if (dispatcher[event]) {
                var items = dispatcher[event].items || [];
                var args = [].slice.call(arguments, 1);

                for (var i = 0; i < items.length; i++) {
                    var listener = items[i];

                    if (listener) {
                        if (listener.oref_ && listener.oref_.invalid_) {
                            continue;
                        }
                        listener.apply(self, args);
                    }
                }
            }
            return self;
        };
        self.emitEvent = function (event, eventObj) {
            if (eventObj._invalid_) {
                return;
            }
            if (dispatcher[event]) {
                var items = dispatcher[event].items || [];
                var args = [].slice.call(arguments, 1);

                for (var i = 0; i < items.length; i++) {

                    if (eventObj._invalid_) {
                        break;
                    }

                    var listener = items[i];

                    if (listener) {
                        if (listener.oref_ && listener.oref_.invalid_) {
                            continue;
                        }
                        listener.apply(self, args);
                    }
                }
            }
            return self;
        };
        self.newEvent = newEvent;
    }

    var eventHolderId = 0;

    var Evento = {
        convert: function (eventHolder) {
            EventDispatcher(eventHolderId++, eventHolder);
            return eventHolder;
        },
        convertEvent: function (obj) {
            if (obj) {
                obj.invalidate = function () {
                    obj._invalid_ = true;
                }
            }
        },
        restoreEvent: function (obj) {
            if (obj) {
                obj.invalidate = undefined;
                obj._invalid_ = undefined;
            }
        },
        newEvent: newEvent
    };

    return Evento;
});